/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * This file is based on:
 * org.json.CDL
 * from the JSON in Java implementation.
 *
 * Copyright (c) 2002 JSON.org
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * The Software shall be used for Good, not Evil.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package org.opencms.json;

/**
 * This provides static methods to convert comma delimited text into a
 * JSONArray, and to covert a JSONArray into comma delimited text. Comma
 * delimited text is a very popular format for data interchange. It is
 * understood by most database, spreadsheet, and organizer programs.
 * <p>
 * Each row of text represents a row in a table or a data record. Each row
 * ends with a NEWLINE character. Each row contains one or more values.
 * Values are separated by commas. A value can contain any character except
 * for comma, unless is is wrapped in single quotes or double quotes.
 * <p>
 * The first row usually contains the names of the columns.
 * <p>
 * A comma delimited list can be converted into a JSONArray of JSONObjects.
 * The names for the elements in the JSONObjects can be taken from the names
 * in the first row.<p>
 *
 */
public final class CDL {

    /**
     * Hidden constructor.<p>
     */
    private CDL() {

        // hidden constructor
    }

    /**
     * Produce a JSONArray of strings from a row of comma delimited values.<p>
     *
     * @param x A JSONTokener of the source text
     * @return A JSONArray of strings
     * @throws JSONException if something goes wrong
     */
    public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {

        JSONArray ja = new JSONArray();
        for (;;) {
            String value = getValue(x);
            if ((value == null) || ((ja.length() == 0) && (value.length() == 0))) {
                return null;
            }
            ja.put(value);
            for (;;) {
                char c = x.next();
                if (c == ',') {
                    break;
                }
                if (c != ' ') {
                    if ((c == '\n') || (c == '\r') || (c == 0)) {
                        return ja;
                    }
                    throw x.syntaxError("Bad character '" + c + "' (" + (int)c + ").");
                }
            }
        }
    }

    /**
     * Produce a JSONObject from a row of comma delimited text, using a
     * parallel JSONArray of strings to provides the names of the elements.<p>
     *
     * @param names A JSONArray of names. This is commonly obtained from the
     *  first row of a comma delimited text file using the rowToJSONArray
     *  method
     * @param x A JSONTokener of the source text
     * @return A JSONObject combining the names and values
     * @throws JSONException if something goes wrong
     */
    public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) throws JSONException {

        JSONArray ja = rowToJSONArray(x);
        return ja != null ? ja.toJSONObject(names) : null;
    }

    /**
     * Produce a comma delimited text row from a JSONArray. Values containing
     * the comma character will be quoted.<p>
     *
     * @param ja A JSONArray of strings
     * @return A string ending in NEWLINE
     */
    public static String rowToString(JSONArray ja) {

        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < ja.length(); i += 1) {
            if (i > 0) {
                sb.append(',');
            }
            Object o = ja.opt(i);
            if (o != null) {
                String s = o.toString();
                if (s.indexOf(',') >= 0) {
                    if (s.indexOf('"') >= 0) {
                        sb.append('\'');
                        sb.append(s);
                        sb.append('\'');
                    } else {
                        sb.append('"');
                        sb.append(s);
                        sb.append('"');
                    }
                } else {
                    sb.append(s);
                }
            }
        }
        sb.append('\n');
        return sb.toString();

    }

    /**
     * Produce a JSONArray of JSONObjects from a comma delimited text string
     * using a supplied JSONArray as the source of element names.<p>
     *
     * @param names A JSONArray of strings
     * @param x A JSONTokener of the source text
     * @return A JSONArray of JSONObjects
     * @throws JSONException if something goes wrong
     */
    public static JSONArray toJSONArray(JSONArray names, JSONTokener x) throws JSONException {

        if ((names == null) || (names.length() == 0)) {
            return null;
        }
        JSONArray ja = new JSONArray();
        for (;;) {
            JSONObject jo = rowToJSONObject(names, x);
            if (jo == null) {
                break;
            }
            ja.put(jo);
        }
        if (ja.length() == 0) {
            return null;
        }
        return ja;
    }

    /**
     * Produce a JSONArray of JSONObjects from a comma delimited text string
     * using a supplied JSONArray as the source of element names.<p>
     *
     * @param names A JSONArray of strings.
     * @param string The comma delimited text
     * @return A JSONArray of JSONObjects
     * @throws JSONException if something goes wrong
     */
    public static JSONArray toJSONArray(JSONArray names, String string) throws JSONException {

        return toJSONArray(names, new JSONTokener(string));
    }

    /**
     * Produce a JSONArray of JSONObjects from a comma delimited text string,
     * using the first row as a source of names.<p>
     *
     * @param x The JSONTokener containing the comma delimited text
     * @return A JSONArray of JSONObjects
     * @throws JSONException if something goes wrong
     */
    public static JSONArray toJSONArray(JSONTokener x) throws JSONException {

        return toJSONArray(rowToJSONArray(x), x);
    }

    /**
     * Produce a JSONArray of JSONObjects from a comma delimited text string,
     * using the first row as a source of names.<p>
     *
     * @param string The comma delimited text
     * @return A JSONArray of JSONObjects
     * @throws JSONException if something goes wrong
     */
    public static JSONArray toJSONArray(String string) throws JSONException {

        return toJSONArray(new JSONTokener(string));
    }

    /**
     * Produce a comma delimited text from a JSONArray of JSONObjects. The
     * first row will be a list of names obtained by inspecting the first
     * JSONObject.<p>
     *
     * @param ja A JSONArray of JSONObjects
     * @return A comma delimited text
     * @throws JSONException if something goes wrong
     */
    public static String toString(JSONArray ja) throws JSONException {

        JSONObject jo = ja.optJSONObject(0);
        if (jo != null) {
            JSONArray names = jo.names();
            if (names != null) {
                return rowToString(names) + toString(names, ja);
            }
        }
        return null;
    }

    /**
     * Produce a comma delimited text from a JSONArray of JSONObjects using
     * a provided list of names. The list of names is not included in the
     * output.<p>
     *
     * @param names A JSONArray of strings
     * @param ja A JSONArray of JSONObjects
     * @return A comma delimited text
     * @throws JSONException if something goes wrong
     */
    public static String toString(JSONArray names, JSONArray ja) throws JSONException {

        if ((names == null) || (names.length() == 0)) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < ja.length(); i += 1) {
            JSONObject jo = ja.optJSONObject(i);
            if (jo != null) {
                sb.append(rowToString(jo.toJSONArray(names)));
            }
        }
        return sb.toString();
    }

    /**
     * Get the next value. The value can be wrapped in quotes. The value can
     * be empty.<p>
     *
     * @param x A JSONTokener of the source text
     * @return The value string, or null if empty
     * @throws JSONException if the quoted string is badly formed
     */
    private static String getValue(JSONTokener x) throws JSONException {

        char c;
        do {
            c = x.next();
        } while ((c == ' ') || (c == '\t'));
        switch (c) {
            case 0:
                return null;
            case '"':
            case '\'':
                return x.nextString(c);
            case ',':
                x.back();
                return "";
            default:
                x.back();
                return x.nextTo(',');
        }
    }
}
